﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using ScrumFactory.Composition.ViewModel;
using ScrumFactory.Composition;
using ScrumFactory.ProjectsList.ViewModel;
using ScrumFactory.Services;


namespace ScrumFactory.ProjectsList.ViewModel.Tests
{

    /// <summary>
    /// Tests the Project List View Model.
    /// </summary>
    [TestClass()]    
    public class ProjectsListViewModelTest {

        [TestMethod()]
        public void Check_FilterResults() {
            // (1) CREATE MOCKS
            var aggregator = SetupAggregator();
            var projectsService = SetupService();
            var executor = SetupExecutor();

            // (2) CREATE TARGET
            var target = new ProjectsListViewModel(aggregator.Object, projectsService.Object, executor.Object);

            // (3) ASSERT TESTS
            // all projects should return project1, project2 and project23
            target.StatusFilter = ProjectStatusFilter.ALL_PROJECTS;
            Assert.IsTrue(((System.Windows.Data.ListCollectionView)target.FilteredProjects).Count == 3);

            // filtering by "2", should return project2 and project23
            target.SearchFilterText = "2";
            target.RefreshProjectListCommand.Execute(null);
            Assert.IsTrue(((System.Windows.Data.ListCollectionView)target.FilteredProjects).Count == 2);

            // filtering by "2 ACME", should return project23
            target.SearchFilterText = "2 ACME";
            target.RefreshProjectListCommand.Execute(null);
            Assert.IsTrue(((System.Windows.Data.ListCollectionView)target.FilteredProjects).Count == 1);
            Assert.IsTrue(target.FilteredProjects.Contains(project23));
            

        }
       
        [TestMethod()]
        public void RefreshProjectList_OnFilterChange()
        {
            // (1) CREATE MOCKS
            var aggregator      = SetupAggregator();
            var projectsService = SetupService();
            var executor        = SetupExecutor();

            // (2) CREATE TARGET
            var target = new ProjectsListViewModel(aggregator.Object, projectsService.Object, executor.Object);

            // (3) ASSERT TESTS
            target.StatusFilter = ProjectStatusFilter.ALL_PROJECTS;
            projectsService.Verify(p => p.GetAllProjects(), "GetAllProjects was not called");            
            
            // set the filter to RUNNING and verifies
            target.StatusFilter = ProjectStatusFilter.RUNNING_PROJECTS;            
            projectsService.Verify(p => p.GetRunningProjects(), "GetRunningProjects was not called");
            
            // others checks
            Assert.IsNotNull(target.Projects, "Projects property is null");
            Assert.AreEqual(3, target.Projects.Count, "Projects count does not match");
            Assert.AreSame(target.Projects[0], project1);
            //Assert.IsNotNull(target.ShowProjectDetailCommand, "ShowProjectDetailsCommand is null");
        }

        [TestMethod()]
        public void LoadProjectList_OnlyOnceOnLoad() {

            // (1) CREATE MOCKS
            var aggregator      = SetupAggregator();
            var projectsService = SetupService();
            var executor        = SetupExecutor();

            // (2) CREATE TARGET
            var target = new ProjectsListViewModel(aggregator.Object, projectsService.Object, executor.Object);

            // (3) ASSERT TESTS
            // set the filter to ALL and verifies
            target.StatusFilter = ProjectStatusFilter.ALL_PROJECTS;
            projectsService.Verify(p => p.GetAllProjects(), "GetAllProjects was not called");

            // try to call command again, it should not get the project again            
            projectsService = new Mock<IProjectsService>();
            bool runtwice = true;
            target.LoadOnceProjectListCommand.Execute(null);
            try {
                projectsService.Verify(p => p.GetAllProjects());
            } catch (Moq.MockException) {
                // if it did not ran again, thats ok.
                runtwice = false;                
            }

            // but if it was called, thats mean shit
            if(runtwice)
                Assert.Fail("GetAllProjects was called twice on load");
            
        }

        [TestMethod()]
        public void PublishesViewProjectDetails_OnShowProjectDetailsCommand()
        {
            // (1) CREATE MOCKS
            var aggregator      = SetupAggregator();
            var projectsService = SetupService();
            var executor        = SetupExecutor();

            // (2) CREATE TARGET
            var target = new ProjectsListViewModel(aggregator.Object, projectsService.Object, executor.Object);

            // (3) ASSERT TESTS
            target.SelectedProject = project1;
            //target.ShowProjectDetailCommand.Execute(null);

            aggregator.Verify(a => a.Publish<Project>(ScrumFactoryEvent.ViewProjectDetails, project1), "ViewProjectDetails was not published");
        }

        [TestMethod()]
        public void CreateNewCommand_ShouldShowCreateView()
        {
            // (1) CREATE MOCKS
            var aggregator = SetupAggregator();
            var projectsService = SetupService();
            var executor = SetupExecutor();

            var createProjectView = new Mock<ICreateProjectView>();

            // (2) CREATE TARGET
            var target = new ProjectsListViewModel(aggregator.Object, projectsService.Object, executor.Object);

            // (3) ASSERT TESTS                                    
            //target.CreateProjectView = createProjectView.Object;
            //Assert.IsNotNull(target.CreateProjectCommand);
            //target.CreateProjectCommand.Execute(null);
            //createProjectView.Verify(v => v.Show());
        }


        #region Mock Setups

        // Mock objects
        Project project1 = new Project { ProjectUId = "project1", ProjectName = "1", ProjectNumber = 1 };
        Project project2 = new Project { ProjectUId = "project2", ProjectName = "2", ProjectNumber = 2 };
        Project project23 = new Project { ProjectUId = "project23", ProjectName = "ACME", ProjectNumber = 23 };

        Mock<IBackgroundExecutor> SetupExecutor() {
            var executor = new Mock<IBackgroundExecutor>();
            executor.Setup(e => e.StartBackgroundTask<ICollection<Project>>(
                It.IsAny<Func<ICollection<Project>>>(),
                It.IsAny<Action<ICollection<Project>>>()))
                .Callback<Func<ICollection<Project>>, Action<ICollection<Project>>>((a, c) => {
                    var r = a();
                    c(r);
                });
            return executor;
        }


        Mock<IProjectsService> SetupService() {
            var projectsService = new Mock<IProjectsService>();
            var projects = new List<Project> { project1, project2, project23 };
            projectsService.Setup(p => p.GetAllProjects()).Returns(projects);
            projectsService.Setup(p => p.GetRunningProjects()).Returns(projects);
            return projectsService;
        }


        Mock<IEventAggregator> SetupAggregator() {
            var aggregator = new Mock<IEventAggregator>();
            return aggregator;
        }

        #endregion
 
    }
}
